package com.lcp.se.config;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lcp.se.entity.data.AppletUser;
import com.lcp.se.mapper.AppletUserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;

/**
 * 用于动态定时刷新 access_token，和向 Redis 添加目标用户
 *
 * @author lcp
 * @date 2020/8/20 21:35
 */
@Configuration
@EnableScheduling
public class DynamicScheduleConfig implements SchedulingConfigurer {
    private final Logger log = LoggerFactory.getLogger(DynamicScheduleConfig.class);

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private AppletUserMapper appletUserMapper;

    @Value("${mini-program.app-id}")
    private String appId;

    @Value("${mini-program.app-secret}")
    private String appSecret;

    @Value("${mini-program.access-token-url}")
    private String accessTokenUrl;

    @Value("${mini-program.push-target-key-redis}")
    private String pushTargetKey;

    private String url;

    // 初始化，此处1000L 对应1秒
    private Long delay = 1000L;

    @PostConstruct
    private void init() {
        url = String.format(accessTokenUrl, appId, appSecret);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 间隔时间动态改变的定时任务，用于刷新 access_token
        taskRegistrar.addTriggerTask(this::refreshAccessToken,
                triggerContext -> new PeriodicTrigger(delay).nextExecutionTime(triggerContext));

        // 间隔时间固定的定时任务，用于刷新 pushTarget，间隔 600 秒
        taskRegistrar.addTriggerTask(this::addPushTarget,
                triggerContext -> new PeriodicTrigger(600000L).nextExecutionTime(triggerContext));
    }

    /**
     * 获取/刷新 access_token
     */
    public void refreshAccessToken() {
        try {
            log.info("尝试获取access_token中...... " + System.currentTimeMillis());
            String s = restTemplate.getForObject(url, String.class);
            JSONObject data = JSONObject.parseObject(s);
            redisTemplate.opsForValue().set("access_token", data.getString("access_token"));
            // 设置下次延迟为失效前5分钟
            delay = (data.getLong("expires_in") - 300) * 1000;
            log.info("获取access_token成功：" + s);
        } catch (Exception e) {
            log.warn("获取access_token失败，1秒后重试：" + e.getMessage());
            this.delay = 1000L;
        }
    }

    /**
     * 添加推送的目标用户
     */
    public void addPushTarget() {
        log.info("刷新 pushTarget");
        QueryWrapper<AppletUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_type", 1);
        List<AppletUser> appletUsers = appletUserMapper.selectList(queryWrapper);
        appletUsers.forEach(appletUser -> {
            redisTemplate.opsForSet().add(pushTargetKey, appletUser.getUserOpenId());
        });
        log.info("刷新 pushTarget 完毕");
    }
}
